import logging
import subprocess as p
import typing as t
from dataclasses import dataclass, field
from pathlib import Path

import rich

from ..panda import CompilerCmd
from . import parser
from .progress import progress_run

_log = logging.getLogger()


class Updater:
    @dataclass
    class Result:
        returncode: int
        changed: bool
        output: str | None
        labels_not_found: set[parser.ErrorLabel] = field(default_factory=set)

    def __init__(self, panda: CompilerCmd):
        self.cmd: CompilerCmd = panda

    def build(self, file: Path):
        return p.run(
            args=self.cmd(file),
            text=True,
            stderr=p.STDOUT,
            stdout=p.PIPE,
        )

    def update(self, file: Path):
        proc = self.build(file)
        if proc.returncode == 1:
            changed, labels_not_found = parser.update(file, proc.stdout.splitlines())
            return Updater.Result(
                returncode=proc.returncode,
                changed=changed,
                labels_not_found=labels_not_found,
                output=None,
            )
        return Updater.Result(
            returncode=proc.returncode,
            changed=False,
            output=proc.stdout,
        )

    def update_files(self, files: list[Path]):
        self._run(files, self.update)

    def _run(self, files: list[Path], func: t.Callable[[Path], Result]):
        console = rich.console.Console(log_path=False, log_time=False)

        def task_run(task: Path):
            func_result = func(task)
            status: str = ''
            output: str | None = None
            match func_result:
                case Updater.Result(returncode=0):
                    status = '[yellow]!'
                case Updater.Result(returncode=1, changed=changed):
                    if changed:
                        status = '[green]U'
                    else:
                        status = ' '
                case Updater.Result(returncode=-2):
                    status = '[red]A'
                    output = func_result.output
                case Updater.Result(returncode=returncode):
                    status = f'[red]{returncode}'
                    output = func_result.output
            labels_str = ''
            if len(func_result.labels_not_found) > 0:
                labels = ','.join([f'[bold]{lbl!s}[/bold]' for lbl in func_result.labels_not_found])
                labels_str = f'[yellow]Unused labels: {labels}[/yellow]'

            file = f'[link={task.resolve().as_uri()}]{task!s}[/link]'
            result = [status, f'{file}{labels_str}', labels_str]
            if output is not None:
                result.append('\n' + output)
            return result

        progress_run(console, files, task_run)
